/*******************************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package com.architexa.org.eclipse.gef.internal.ui.palette.editparts;

import com.architexa.org.eclipse.draw2d.IFigure;
import com.architexa.org.eclipse.draw2d.ToolbarLayout;
import com.architexa.org.eclipse.draw2d.geometry.Dimension;
import com.architexa.org.eclipse.draw2d.geometry.Rectangle;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


/**
 * A ToolbarLayout-like layout for the palette.  This layout is palette-specific and 
 * should not be used externally.  This layout only works when vertically oriented.
 * 
 * @author Pratik Shah
 */
public class PaletteToolbarLayout 
	extends ToolbarLayout 
{

/**
 * A figure is growing if it's an expanded drawer.
 * 
 * @param 	child	The figure that is to be marked as growing or non-growing
 * @return	<code>true</code> if the given child is considered growing
 */
protected boolean isChildGrowing(IFigure child) {
	return child instanceof DrawerFigure && ((DrawerFigure)child).isExpanded();
}

public void layout(IFigure parent) {
	List children = parent.getChildren();
	List childrenGrabbingVertical = new ArrayList();
	int numChildren = children.size();
	Rectangle clientArea = parent.getClientArea();
	int x = clientArea.x;
	int y = clientArea.y;
	int availableHeight = clientArea.height;
	boolean stretching;
	Dimension prefSizes [] = new Dimension[numChildren];
	Dimension minSizes [] = new Dimension[numChildren];
	int totalHeight = 0, totalMinHeight = 0, heightOfNonGrowingChildren = 0, 
		heightPerChild = 0, excessHeight = 0;
	
	/*
	 * Determine hints.
	 */
	int wHint = clientArea.width;
	int hHint = -1;

	/*
	 * Store the preferred and minimum sizes of all figures.  Determine which figures can
	 * be stretched/shrunk.
	 */
	for (int i = 0; i < numChildren; i++) {
		IFigure child = (IFigure)children.get(i);
		
		prefSizes[i] = child.getPreferredSize(wHint, hHint);
		minSizes[i] = child.getMinimumSize(wHint, hHint);
		
		totalHeight += prefSizes[i].height;
		totalMinHeight += minSizes[i].height;
						
		if (isChildGrowing(child)) {
			childrenGrabbingVertical.add(child);
		} else {
			heightOfNonGrowingChildren += prefSizes[i].height;
		}
	}
	totalHeight += (numChildren - 1) * getSpacing();
	totalMinHeight += (numChildren - 1) * getSpacing();

	/*
	 * This is the algorithm that determines which figures need to be compressed/stretched
	 * and by how much.
	 */
	stretching = totalHeight - Math.max(availableHeight, totalMinHeight) < 0;
	if (stretching) {
		/*
		 * We only want the last child to stretch.  So, we mark all but the last
		 * growing child as non-growing.
		 */
		for (int i = 0; i < childrenGrabbingVertical.size() - 1; i++) {
			int index = children.indexOf(childrenGrabbingVertical.get(i));
			heightOfNonGrowingChildren += prefSizes[index].height;
		}
		if (!childrenGrabbingVertical.isEmpty()) {
			Object last = childrenGrabbingVertical
					.get(childrenGrabbingVertical.size() - 1);
			childrenGrabbingVertical.clear();
			childrenGrabbingVertical.add(last);
			heightPerChild = availableHeight - heightOfNonGrowingChildren;
		}
	} else if (!childrenGrabbingVertical.isEmpty()) {
		/*
		 * So long as there is at least one child that can be grown, figure out how much
		 * height should be given to each growing child.
		 */
		boolean childrenDiscarded;
		// spaceToConsume is the space height available on the palette that is to be
		// shared by the growing children.
		int spaceToConsume = availableHeight - heightOfNonGrowingChildren;
		// heightPerChild is the height that each growing child is to be grown up to
		heightPerChild = spaceToConsume / childrenGrabbingVertical.size();
		// excessHeight is the space leftover at the bottom of the palette after each
		// growing child has been grown by heightPerChild.
		excessHeight = spaceToConsume - (heightPerChild * childrenGrabbingVertical.size());
		do {
			childrenDiscarded = false;
			for (Iterator iter = childrenGrabbingVertical.iterator(); iter.hasNext();) {
				IFigure childFig = (IFigure)iter.next();
				int i = childFig.getParent().getChildren().indexOf(childFig);
				// In the case of shrinking, if the child's height is less than
				// heightPerChild, mark that child as non-growing
				if (prefSizes[i].height < heightPerChild) {
					spaceToConsume -= prefSizes[i].height;
					heightOfNonGrowingChildren += prefSizes[i].height;
					iter.remove();
					if (!childrenGrabbingVertical.isEmpty()) {
						childrenDiscarded = true;
						heightPerChild = spaceToConsume / childrenGrabbingVertical.size();
						excessHeight = spaceToConsume 
								- (heightPerChild * childrenGrabbingVertical.size());
					}
					break;
				}
			}
		} while (childrenDiscarded);
	}	

	/*
	 * Do the actual layout, i.e. set the bounds of all the figures.
	 */
	for (int i = 0; i < numChildren; i++) {
		IFigure child = (IFigure)children.get(i);
		Rectangle newBounds = new Rectangle(x, y, prefSizes[i].width, prefSizes[i].height);

		if (childrenGrabbingVertical.contains(child)) {
			// Set the height of growing children.  If this is the last one, give it
			// the excess height.
			childrenGrabbingVertical.remove(child);
			if (childrenGrabbingVertical.isEmpty())
				newBounds.height = heightPerChild + excessHeight;
			else
				newBounds.height = heightPerChild;
		}

		if (getStretchMinorAxis())
			newBounds.width = clientArea.width;
		else
			newBounds.width = Math.min(prefSizes[i].width, clientArea.width);

		int adjust = clientArea.width - newBounds.width;
		switch (getMinorAlignment()) {
			case ALIGN_TOPLEFT: 
				adjust = 0;
				break;
			case ALIGN_CENTER:
				adjust /= 2;
				break;
			case ALIGN_BOTTOMRIGHT:
				break;
		}
		newBounds.x += adjust;
		child.setBounds(newBounds);
		y += newBounds.height + getSpacing();
	}
}

}
